package com.tao.system.service.exam.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.tao.common.creo.constants.CacheConstants;
import com.tao.common.creo.constants.Constants;
import com.tao.common.creo.enums.ResultCode;
import com.tao.common.redis.service.RedisService;
import com.tao.common.security.exception.ServiceException;
import com.tao.system.domain.exam.Exam;
import com.tao.system.domain.exam.ExamQuestion;
import com.tao.system.domain.exam.dto.ExamAddDTO;
import com.tao.system.domain.exam.dto.ExamQueryDTO;
import com.tao.system.domain.exam.dto.ExamQuestionAddDTO;
import com.tao.system.domain.exam.vo.ExamDetailVO;
import com.tao.system.domain.exam.vo.ExamVO;
import com.tao.system.domain.question.Question;
import com.tao.system.domain.question.vo.QuestionVO;
import com.tao.system.mapper.exam.ExamMapper;
import com.tao.system.mapper.exam.ExamQuestionMapper;
import com.tao.system.mapper.question.QuestionMapper;
import com.tao.system.service.exam.IExamService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper, ExamQuestion> implements IExamService  {

    @Autowired
    ExamMapper examMapper;
    @Autowired
    QuestionMapper questionMapper;

    @Autowired
    RedisService redisService;

    @Autowired
    ExamQuestionMapper examQuestionMapper;


    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());

        List<ExamVO> exams = examMapper.selectExamList(examQueryDTO);

        return exams;
    }

    @Override
    public String add(ExamAddDTO examAddDTO) {
        // 判断竞赛标题是否重复
        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle, examAddDTO.getTitle()));
        if(CollectionUtil.isNotEmpty(exams)) {
            throw new ServiceException(ResultCode.FAILED_QUESTION_EXISTS);
        }
        // 校验开始时间和结束时间的合法性
        if(examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if(examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);

        }
        Exam exam = new Exam();
        BeanUtil.copyProperties(examAddDTO, exam);
        // 添加数据
        examMapper.insert(exam);
        return exam.getExamId().toString();
    }

    @Override
    public Boolean questionAdd(ExamQuestionAddDTO questionAddDTO) {
        // 获取标题
        Exam exam = getExam(questionAddDTO.getExamId());
        // 校验是否已经开赛
        checkExam(exam);
        // 判断是否传题目
        Set<Long> questionIdSet = questionAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)) {
            return true;
        }
        // 获取题目列表
        List<Question> questions = questionMapper.selectBatchIds(questionIdSet);
        // 如果查询题目出来为空 或者 查询出来的题目的比传过来的少, 就说明有题目不存在
        if(CollectionUtil.isEmpty(questions) || questions.size() < questionIdSet.size()) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXISTS);
        }
        return saveExamQuestion(exam, questionIdSet);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {

        // 1. 获取竞赛详情
        Exam exam = getExam(examId);
        // 2. 判断竞赛是否开赛
        checkExam(exam);
        // 3. 判断题目是否发布
        if(Constants.TRUE.equals(exam.getStatus())) {
            new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        // 3. 删除竞赛中的题目
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }



    @Override
    public ExamDetailVO detail(Long examId) {
        // 获取竞赛信息
        Exam exam = getExam(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtil.copyProperties(exam, examDetailVO);
        // 获取竞赛下的题目列表
        List<QuestionVO> questionVOS = examQuestionMapper.selectExamQuestionList(examId);
        if(questionVOS == null) {
            return examDetailVO;
        }
        examDetailVO.setExamQuestionList(questionVOS);
        return examDetailVO;
    }

    @Override
    public int publish(Long examId) {
        //1. 根据竞赛id获取竞赛
        Exam exam = getExam(examId);
        //2. 校验竞赛是否合法, 竞赛未结束, 竞赛未开赛, 竞赛下包含题目
        checkExam(exam);
        // 校验竞赛下是否有题目
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if(count == null || count <= 0) {
            throw new ServiceException(ResultCode.EXAM_QUESTION_NULL);
        }
        //3. 修改竞赛状态
        exam.setStatus(Constants.TRUE);
        // 4. 将竞赛信息存储到redis中
        redisService.leftPushForList(CacheConstants.EXAM_UNFINISHED_LIST, exam.getExamId());
        redisService.leftPushForList(CacheConstants.EXAM_DETAIL + exam.getExamId(), exam);

        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        // 1. 获取竞赛
        Exam exam = getExam(examId);
        // 2. 校验竞赛是否开赛
        checkExam(exam);
        // 4. 校验竞赛是否发布
        if(Constants.FALSE.equals(exam.getStatus())) {
            throw new ServiceException(ResultCode.EXAM_NOT_PUBLISH);
        }
        // 修改竞赛为未发布
        exam.setStatus(Constants.FALSE);
        // 删除redis中的竞赛信息
        redisService.leftPushForList(CacheConstants.EXAM_USER_LIST + exam.getExamId(), exam); // 删除详情
        redisService.leftPushForList(CacheConstants.EXAM_UNFINISHED_LIST, exam.getExamId()); // 从未完赛中删除

        return examMapper.updateById(exam);
    }

    @Override
    public int delete(Long examId) {
        // 获取竞赛
        Exam exam = getExam(examId);
        // 校验竞赛
        checkExam(exam);
        // 删除竞赛下包含的题目
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        // 删除竞赛
        return examMapper.deleteById(examId);
    }


    /**
     * 校验是否已经开赛
     */
    private void checkExam(Exam exam) {
        // 判断是否完赛
        if(exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_END);
        }
        if(exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ServiceException(ResultCode.EXAM_IS_START_EXISTS);
        }
    }

    /**
     * 批量插入题目
     * @return 是否插入成功
     */
    private boolean saveExamQuestion(Exam exam, Set<Long> questionIdSet) {
        // 接下来批量插入题目
        int num = 1;
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for(Long questionId : questionIdSet) {
            ExamQuestion question = new ExamQuestion();
            question.setQuestionId(questionId);
            question.setExamId(exam.getExamId());
            question.setQuestionOrder(num++);
            examQuestionList.add(question);
        }
        return saveBatch(examQuestionList);
    }
    public Exam getExam(Long ExamId) {
        Exam exam = examMapper.selectById(ExamId);
        if(exam == null) {
            throw new ServiceException(ResultCode.EXAM_NOT_EXISTS);
        }
        return exam;
    }
}
